/*
 * OGLContext.cpp
 *
 *  Created on: 15 févr. 2011
 *      Author: seeme
 */

#include "OGLContext.h"
#include "Memory/Resources/Models/Diamond.h"

float theta = 0.;


OGLContext::OGLContext() {
	m_camera.update(0, 0, -40, 0, 0, 0, 0, 1, 0);
}

OGLContext::~OGLContext() {
	glDeleteProgram(m_program);
	m_currentLvl->downRefCount();
}

void OGLContext::sendEyePos(GLuint program){

	Vect4<float> eyepos = m_camera.m_position;

	glUniform4f(glGetUniformLocation(program, "eyepos"),
			eyepos.x,
			eyepos.y,
			eyepos.z,
			eyepos.w);
}

void OGLContext::render(){
	//Log::i("OGLContext") << "Rendering...";

	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	//glUseProgram(m_program);

	glEnable(GL_DEPTH_TEST);

	lookAt( 0, 0, -100, 0, 0, 0, 0, 1, 0 );

	//glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);

	//rotate(theta, 0, 1, 0);
	theta += .1;
	if(theta > 360) theta = 0.;

	pushMatrix();
			rotate( -theta, 0.1, 1, 0 );
			drawGizmos();
	popMatrix();

	pushMatrix();
		rotate( theta, 1, 1, 1 );
		drawGraph(m_currentLvl->getGraph());
	popMatrix();


	App.Display();
}


void OGLContext::drawGizmos(){
	//On dessine les lampes
	//glDepthFunc(GL_LESS);
	//glEnable(GL_BLEND);
	//glBlendFunc(GL_ONE, GL_ZERO);
	std::vector<OmniLight*> lights = m_currentLvl->getOmniLights();
	for(std::vector<OmniLight*>::iterator light = lights.begin(); light != lights.end(); ++light){
		glEnable(GL_TEXTURE_2D);
		StaticDrawableEntity* gyzmo = (*light)->getGyzmo();

		GLuint program = gyzmo->getProgramId("AMBIANT");
		if(program > 0){
			pushMatrix();
			glUseProgram(program);

			sendEyePos(program);

			//Envoie de la MVP
			m_model =  gyzmo->getTransfos(m_model);


			updateMatrices(program);
			gyzmo->draw("AMBIANT");
			popMatrix();
		}
		glDisable(GL_TEXTURE_2D);
	}
	//glDisable(GL_BLEND);
}

void OGLContext::renderAmbiant(sNode<StaticDrawableEntity*>& node){
	//Rendu ambiant

	GLuint program = node.getValue()->getProgramId("AMBIANT");
		if(program > 0){
		pushMatrix();
		glUseProgram(program);


		//Envoie de la lumière ambiante
		AmbiantLight* ambiant = m_currentLvl->getAmbiantLight();
		glUniform4f(glGetUniformLocation(program, "aColor"),
				ambiant->getColor().x,
				ambiant->getColor().y,
				ambiant->getColor().z,
				ambiant->getColor().w);
		//Transformations
		m_model =  node.getValue()->getTransfos(m_model);

		  //Envoie de la MVP
		updateMatrices(program);
		node.getValue()->draw("AMBIANT");
		popMatrix();
	}
}

void OGLContext::renderDiffuseLight(sNode<StaticDrawableEntity*>& node){

	GLuint program = node.getValue()->getProgramId("SPECULAR");
	if(program > 0){
		glUseProgram(program);

		std::vector<OmniLight*> lights = m_currentLvl->getOmniLights();
		for(std::vector<OmniLight*>::iterator light = lights.begin(); light != lights.end(); ++light){
			pushMatrix();
			//On envoie la lampe au shader
			(*light)->getPosition().normalize();
			glUniform4f(glGetUniformLocation(program, "lightpos"),
					(*light)->getPosition().x,
					(*light)->getPosition().y,
					(*light)->getPosition().z,
					(*light)->getPosition().w);

			glUniform4f(glGetUniformLocation(program, "lightcolor"),
					(*light)->getColor().x,
					(*light)->getColor().y,
					(*light)->getColor().z,
					(*light)->getColor().w);

			//Transformations
			m_model =  node.getValue()->getTransfos(m_model);

			//Position de l'oeuil
			sendEyePos(program);

			//Envoie de la MVP
			updateMatrices(program);
			node.getValue()->draw("SPECULAR");
			popMatrix();
		}
	}
}


void OGLContext::drawGraph(sNode<StaticDrawableEntity*>& node){
	if(node.getValue() != NULL){
		glEnable(GL_DEPTH_TEST);
		glDisable(GL_BLEND);
		glDepthFunc(GL_LEQUAL);
		glDepthMask(GL_TRUE);

		renderAmbiant(node);

		glEnable(GL_BLEND);
		glBlendFunc (GL_ONE, GL_ONE);

		//glDisable(GL_DEPTH_TEST);
		//glDepthMask(GL_FALSE);


		renderDiffuseLight(node);
		glDisable(GL_BLEND);
	}


	std::list<sNode<StaticDrawableEntity*>* > children = node.getChildren();

	if(children.size() > 0){
		std::list<sNode<StaticDrawableEntity*>* >::iterator iter = children.begin();
		for( ; iter != children.end(); iter++)
			drawGraph(**iter);
	}
}

void OGLContext::reshape(int width, int height){
	  glViewport(0, 0, width, height);

	  float a = width / static_cast<GLfloat>(height);
	  setPerspective(45.0f, a, 0.1f, 200.0f);
}

void OGLContext::openContext(int width, int height, const char* title){

	App.Create(sf::VideoMode(width, height, 32), title);

	if (GLEW_OK != glewInit())
		Log::e("OGLContext") << "Cannot initialize Glew context";

	if(!glewIsSupported("GL_ARB_vertex_buffer_object"))
		Log::e("OGLContext") << "VBO not supported, go get yourself a real cg...";

	if(!glewIsSupported("GL_NV_transform_feedback"))
		Log::i("OGLContext") << "Feedback not supported, won't be able to debug shaders";

	//You cannot call this until glew is launched!!! (took me 2 hours to figure it out..)
	m_currentLvl = ((Level*)Singleton<ResCache>::get()->getResource("test.cubelvl"));
	//m_currentLvl = ((Level*)Singleton<ResCache>::get()->getResource("test.planelvl"));

	//TEST: on essaye d'ajouter un diamant à la scène...
	//Diamond* diam = new Diamond("test.diam1", 3.);
	//delete diam;

	Log::i("CTX") << "Deleted";

	glClearColor(0.3f,0.3f,0.3f,1.f);

	glEnable(GL_DEPTH_TEST);

	glFrontFace(GL_CCW);

	//Load shaders
	//loadShaders();

	m_view.setColumn(Vect4<float>(1., 1., 1., 1.), 0);

	reshape(width, height);
	run();
}

void OGLContext::run(){
	while (App.IsOpened())
	{
	    sf::Event Event;
	    while (App.GetEvent(Event))
	    {
	    	if (Event.Type == sf::Event::Resized)
	    			reshape(Event.Size.Width, Event.Size.Height);

	    	// Close window : exit
			if (Event.Type == sf::Event::Closed)
				App.Close();

			// Escape key : exit
			if ((Event.Type == sf::Event::KeyPressed) && (Event.Key.Code == sf::Key::Escape))
				App.Close();
	    }
	    render();
	}
}

void OGLContext::keyPressEvent(unsigned char key, int x, int y){
	Singleton<UserInputState>::get()->keyPressed(key);
}

void OGLContext::keyUpEvent(unsigned char key, int x, int y){
	Singleton<UserInputState>::get()->keyReleased();
}

void OGLContext::idle(){}

void OGLContext::loadShaders(){
	GLint status;
	char errMsg[256];
	GLint len;

	Shader* vShader = (Shader*)Singleton<ResCache>::get()->getResource("test.vShader");
	Shader* fShader = (Shader*)Singleton<ResCache>::get()->getResource("test.fShader");

	m_program = glCreateProgram();

	glAttachShader(m_program, vShader->getShaderId());
	glAttachShader(m_program, fShader->getShaderId());

	glLinkProgram(m_program);
	glGetProgramiv(m_program, GL_LINK_STATUS, &status);


	if(GL_TRUE != status){
		glGetProgramInfoLog(m_program, 256, &len, errMsg);

		Log::e("OGLContext") << "Can't link program: " << errMsg;
	}

	//We don't need the shader's file anymore
	vShader->downRefCount();
	fShader->downRefCount();
	//glUseProgram(m_program);
}




